﻿using Modbus.Device;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace JGSY.Monitor.Greenhouse
{
    public partial class FrmMain : Form
    {

        private SerialPort serialPort;//串口对象
        private IModbusMaster master;//主站对象

        //温度、湿度、亮度相关变量
        private double temperatureValue = 0.0;
        private double temperatureMin = 100;
        private double temperatureMax = 0.0;
        private double humidityValue = 0.0;
        private double humidityMin = 100;
        private double humidityMax = 0.0;
        private double brightnessValue = 0.0;
        private double brightnessMin = 100;
        private double brightnessMax = 0.0;

        public FrmMain()
        {
            InitializeComponent();
            InitializeSerialPort();
        }

        #region SerialPort


        private void InitializeSerialPort()
        {
            serialPort = new SerialPort();
            //串口下拉框
            RefreshPortNames();
            //波特率
            InitBaudRates();
            //检验位
            InitParitys();
            //数据位
            InitDataBits();
            //停止位
            InitStopBits();
        }

        private void InitStopBits()
        {
            foreach (var stopBit in Enum.GetNames(typeof(StopBits)))
            {
                comboStopBits.Items.Add(stopBit);
            }
            comboStopBits.SelectedItem = "One";

        }

        private void InitDataBits()
        {
            int[] baudrates = new int[]
           {
                4800,
                9600,
                14400,
                19200,
                38400,
                57600,
                115200,
                128000
           };
            foreach (var rate in baudrates)
            {
                comboDataBits.Items.Add(rate);
            }
            comboDataBits.SelectedItem = 9600;
        }

        private void InitParitys()
        {
            foreach (var parity in Enum.GetNames(typeof(Parity)))
            {
                comboParitys.Items.Add(parity);
            }
            comboParitys.SelectedItem = "None";
        }

        private void InitBaudRates()
        {
            int[] databits = new int[] { 7, 8 };
            foreach (var data in databits)
            {
                comboBaudRates.Items.Add(data);
            }
            comboBaudRates.SelectedItem = 8;
        }

        private void RefreshPortNames()
        {

            comboPorts.Items.Clear();
            var portVolues = SerialPort.GetPortNames();
            foreach (var portVolue in portVolues)
            {
                comboPorts.Items.Add(portVolue);
            }
            if (comboPorts.Items.Count > 0)
            {
                comboPorts.SelectedIndex = 0;//默认选择第一个端口
            }
        }
        #endregion

        #region Load

        private void FrmMain_Load(object sender, EventArgs e)
        {
            InitializeInfo();
        }


        private void InitializeInfo()
        {
            //图表初始化
            InitializePlots();
            //页面信息初始化
            InitializeWindowsData();
        }


        PlotModel plotModel = null;
        DateTimeAxis dateTimeAxis;//x轴
        LinearAxis valueAxis;//y轴
        Random random = new Random();
        private void InitializePlots()
        {
            plotModel = new PlotModel()
            {
                IsLegendVisible = false,
                PlotAreaBorderColor = OxyColors.LightGray,
                PlotAreaBackground = OxyColors.White
            };
            //x轴
            dateTimeAxis = new DateTimeAxis()
            {
                MajorGridlineStyle = LineStyle.Dash,
                MajorGridlineThickness = 0,
                IntervalLength = 5,
                IntervalType = DateTimeIntervalType.Seconds,
                FontSize = 10,
                TextColor = OxyColors.Gray,
                IsPanEnabled = false,
                IsZoomEnabled = false,
                AxislineColor = OxyColors.LightGray,
                StringFormat = "MM:ss"
            };
            plotModel.Axes.Add(dateTimeAxis);
            //y轴
            valueAxis = new LinearAxis()
            {
                MajorGridlineStyle = LineStyle.Dash,
                IntervalLength = 20,
                IsZoomEnabled = false,
                IsPanEnabled = false,
                Maximum = 110,
                Minimum = -20,
                FontSize = 10,
                TextColor = OxyColors.Gray,
                AxislineColor = OxyColors.LightGray,
            };
            plotModel.Axes.Add(valueAxis);

            //添加三条曲线序列
            var series01 = new LineSeries()
            {
                Color = OxyColors.Orange,
                StrokeThickness = 1,
                MarkerType = MarkerType.None,
                Title = "温度",
                InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline
            };
            var series02 = new LineSeries()
            {
                Color = OxyColors.Green,
                StrokeThickness = 1,
                MarkerType = MarkerType.None,
                Title = "湿度",
                InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline
            };
            var series03 = new LineSeries()
            {
                Color = OxyColors.SpringGreen,
                StrokeThickness = 1,
                MarkerType = MarkerType.None,
                Title = " 亮度",
                InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline
            };
            DateTime dt = DateTime.Now.AddSeconds(-20);
            plotModel.Axes[0].Minimum = DateTimeAxis.ToDouble(dt);
            for (int i = 0; i < 20; i++)
            {
                dt = dt.AddSeconds(1);
                series01.Points.Add(new DataPoint(DateTimeAxis.ToDouble(dt), random.Next(0, 100)));
                series02.Points.Add(new DataPoint(DateTimeAxis.ToDouble(dt), random.Next(0, 100)));
                series03.Points.Add(new DataPoint(DateTimeAxis.ToDouble(dt), random.Next(0, 100)));
            }
            plotModel.Axes[0].Maximum = DateTimeAxis.ToDouble(dt);
            plotModel.Series.Add(series01);
            plotModel.Series.Add(series02);
            plotModel.Series.Add(series03);
            pvOne.Model = plotModel;
        }

        private void InitializeWindowsData()
        {
            //仪表值重置为0
            mctWen.Value = 0;
            mctShi.Value = 0;
            mctLiang.Value = 0;
            //历史值重置0、启动模拟状态为false
            lblWen.Text = "0.0";
            lblWen1.Text = "0.0";
            ckbWen3.Checked = false;

            lblshi1.Text = "0";
            lblshi.Text = "0";
            ckbshi2.Checked = false;

            lblliang.Text = "0";
            lblliang1.Text = "0";
            ckbliang2.Checked = false;

            //曲线显示状态为true
            checkBox4.Checked = true;
            checkBox5.Checked = true;
            checkBox6.Checked = true;

            //显示屏区初始化
            lblShowMsg.Text = "";
            textBox1.Text = "";

            //状态显示区
            chkState01.Checked = false;
            chkState02.Checked = false;
            chkState03.Checked = false;
            chkState04.Checked = false;
            chkState05.Checked = false;
            chkStateAll.Checked = false;
        }
        #endregion

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            RefreshPortNames();
        }

        private void FrmMain_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(Pens.LightGray, 1, 1, this.Width - 2, this.Height - 2);
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(Pens.LightGray, 0, 0, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1);
        }



        #region 窗口拖动
        Point mPoint = new Point();
        bool isMove = false;
        private void panelTop_MouseDown(object sender, MouseEventArgs e)
        {
            mPoint = e.Location;//当前按下的点
            isMove = true;//启动拖动
        }

        private void panelTop_MouseMove(object sender, MouseEventArgs e)
        {
            if (isMove)
            {
                this.Location = new Point(this.Location.X + e.X - mPoint.X, this.Location.Y + e.Y - mPoint.Y);
            }
        }

        private void panelTop_MouseUp(object sender, MouseEventArgs e)
        {
            isMove = false;
        }
        #endregion

        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnConnectDevice_Click(object sender, EventArgs e)
        {

            try
            {
                if (serialPort.IsOpen)//已连接
                {
                    //断开处理
                    serialPort.Close();
                    btnConnectDevice.Text = "连接设备";
                    //更新相关控件的可用性
                    SetControlsEnabled(false);
                    lblTipMsg.Text = "已断开温室大棚监控连接!";
                    lblTipMsg.ForeColor = Color.ForestGreen;
                }
                else  //未连接
                {
                    //连接处理
                    serialPort.PortName = comboPorts.Text;
                    serialPort.BaudRate = int.Parse(comboDataBits.Text);
                    serialPort.Parity = (Parity)Enum.Parse(typeof(Parity), comboParitys.Text);
                    serialPort.DataBits = int.Parse(comboBaudRates.Text);
                    serialPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), comboStopBits.Text);
                    serialPort.Open();//打开连接
                    btnConnectDevice.Text = "断开连接";
                    //更新相关控件的可用性
                    SetControlsEnabled(true);
                    //创建主站对象
                    master = ModbusSerialMaster.CreateRtu(serialPort);

                    //开始监控温室大棚数据
                    StartMonitor();
                    lblTipMsg.Text = "连接成功，正在监控温室大棚数据信息！";
                    lblTipMsg.ForeColor = Color.ForestGreen;
                }
            }
            catch (Exception ex)
            {
                lblTipMsg.Text = $"[异常]{ex.Message}";
                lblTipMsg.ForeColor = Color.DarkRed;
            }
        }

        private void SetControlsEnabled(bool isConnected)
        {
            if (isConnected)
            {
                comboPorts.Enabled = false;
                comboBaudRates.Enabled = false;
                comboParitys.Enabled = false;
                comboDataBits.Enabled = false;
                comboStopBits.Enabled = false;

                chkState01.Enabled = true;
                chkState02.Enabled = true;
                chkState03.Enabled = true;
                chkState04.Enabled = true;
                chkState05.Enabled = true;
                chkStateAll.Enabled = true;

                btnSend.Enabled = true;
            }
            else
            {
                comboPorts.Enabled = true;
                comboBaudRates.Enabled = true;
                comboParitys.Enabled = true;
                comboDataBits.Enabled = true;
                comboStopBits.Enabled = true;

                chkState01.Enabled = false;
                chkState02.Enabled = false;
                chkState03.Enabled = false;
                chkState04.Enabled = false;
                chkState05.Enabled = false;
                chkStateAll.Enabled = false;

                btnSend.Enabled = false;
            }
        }


        int[] lastRandoms = new int[3];
        bool[] order = new bool[3] { true, true, true };
        bool isWriting = false;//是否写的状态
        private void StartMonitor()
        {
            Task.Run(async () =>
            {
                while (serialPort.IsOpen)
                {
                    try
                    {
                        //读取温度、湿度、亮度 ReadHoldingRegistersAsync
                        ushort[] datas = await master.ReadInputRegistersAsync(1, 0, 3);
                        if (datas.Length > 0)
                        {
                            //温度值处理
                            if (ckbWen3.Checked)
                            {
                                //生成模拟数据
                                GenarateRandom(ref lastRandoms[0], ref order[0], -10, 90);
                                temperatureValue = lastRandoms[0];
                            }
                            else
                            {
                                temperatureValue = datas[0] * 0.1f;
                            }
                            if (temperatureMin > temperatureValue)
                                temperatureMin = temperatureValue;
                            if (temperatureMax < temperatureValue)
                                temperatureMax = temperatureValue;

                            //湿度值处理
                            if (ckbshi2.Checked)
                            {
                                //生成模拟数据
                                GenarateRandom(ref lastRandoms[1], ref order[1], 0, 100);
                                humidityValue = lastRandoms[1];
                            }
                            else
                            {
                                humidityValue = datas[1];
                            }
                            if (humidityMin > humidityValue)
                                humidityMin = humidityValue;
                            if (humidityMax < humidityValue)
                                humidityMax = humidityValue;

                            //亮度值处理
                            if (ckbliang2.Checked)
                            {
                                //生成模拟数据
                                GenarateRandom(ref lastRandoms[2], ref order[2], 0, 100);
                                brightnessValue = lastRandoms[2];
                            }
                            else
                            {
                                brightnessValue = datas[2];
                            }
                            if (brightnessMin > brightnessValue)
                                brightnessMin = brightnessValue;
                            if (brightnessMax < brightnessValue)
                                brightnessMax = brightnessValue;

                        }
                        //灯珠状态
                        bool[] blStates = await master.ReadCoilsAsync(1, 0, 5);

                        this.Invoke(new Action(() =>
                        {
                            //呈现仪表值
                            mctWen.Value = temperatureValue;
                            mctShi.Value = humidityValue;
                            mctLiang.Value = brightnessValue;
                            //历史值的呈现
                            lblWen.Text = temperatureMax.ToString("##.#");
                            lblWen1.Text = temperatureMin.ToString("##.#");
                            lblshi.Text = humidityMax.ToString("##.#");
                            lblshi1.Text = humidityMin.ToString("##.#");
                            lblliang.Text = brightnessMax.ToString("##.#");
                            lblliang1.Text = brightnessMin.ToString("##.#");
                            //更新灯珠的状态
                            if (!isWriting)
                            {
                                chkState01.Checked = blStates[0];
                                chkState02.Checked = blStates[1];
                                chkState03.Checked = blStates[2];
                                chkState04.Checked = blStates[3];
                                chkState05.Checked = blStates[4];
                                chkStateAll.Checked = !blStates.ToList().Exists(b => !b);
                            }

                            //图表刷新
                            DateTime dt = DateTime.Now;
                            pvOne.Model.Axes[0].Minimum = DateTimeAxis.ToDouble(dt.AddSeconds(-20));
                            pvOne.Model.Axes[0].Maximum = DateTimeAxis.ToDouble(dt);

                            var lineSeries = pvOne.Model.Series[0] as LineSeries;
                            lineSeries.Points.Add(new DataPoint(DateTimeAxis.ToDouble(dt), temperatureValue));
                            if (lineSeries.Points.Count > 20)
                            {
                                lineSeries.Points.RemoveAt(0);
                            }

                            var lineSeries1 = pvOne.Model.Series[1] as LineSeries;
                            lineSeries1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(dt), humidityValue));
                            if (lineSeries1.Points.Count > 20)
                            {
                                lineSeries1.Points.RemoveAt(0);
                            }

                            var lineSeries2 = pvOne.Model.Series[2] as LineSeries;
                            lineSeries2.Points.Add(new DataPoint(DateTimeAxis.ToDouble(dt), brightnessValue));
                            if (lineSeries2.Points.Count > 20)
                            {
                                lineSeries2.Points.RemoveAt(0);
                            }
                            pvOne.InvalidatePlot(false);
                        }));
                        await Task.Delay(1000);
                    }
                    catch (Exception ex)
                    {
                        lblTipMsg.Text = $"[异常]{ex.Message}";
                        lblTipMsg.ForeColor = Color.DarkRed;
                    }
                }
            });
        }

        private async void btnSend_Click(object sender, EventArgs e)
        {
            if (!serialPort.IsOpen) return;
            try
            {
                string sendText = textBox1.Text;//接收要发送的文本
                if (string.IsNullOrEmpty(sendText))
                    throw new Exception("请输入有效英文字符");
                if (sendText.Length > 60)
                    throw new Exception("输入的字符长度不能超过60");
                if (sendText.ToList().Exists(s => (int)s > 127))
                    throw new Exception("包含无效字符");

                ushort[] datas = GetUshortData(sendText);

                //写入设备
                await master.WriteMultipleRegistersAsync(1, 0, datas);
                Task.Delay(100).Wait();

                lblShowMsg.Text = sendText;
            }
            catch (Exception ex)
            {
                lblTipMsg.Text = $"[异常]{ex.Message}";
                lblTipMsg.ForeColor = Color.DarkRed;
            }
        }



        /// <summary>
        ///随机生成数据方法
        ///</summary>
        ///<param name="randomValue">最终生成值</param>
        ///<param name="order">随机数据的创建方向，往大方向 还是往小方向 </param>
        ///<param name="min">最小值</param>
        ///<param name="max">最大值</param>
        private void GenarateRandom(ref int randomValue, ref bool order, int min, int max)
        {
            if (randomValue == 0)
            {
                randomValue = random.Next(Math.Min(min, max), Math.Max(min, max));
            }
            else
            {
                int rv = randomValue;
                if (order)
                {
                    var _max = Math.Min(rv + 20, max);
                    randomValue = random.Next(rv - 5, _max - 1);
                    if (_max == max)
                        order = false;
                }
                else
                {
                    var _min = Math.Max(rv - 20, min);
                    randomValue = random.Next(_min + 1, rv + 5);
                    if (_min == min)
                        order = true;
                }
            }
        }


     

        /// <summary>
        /// 将字符串转换成ushort数组
        /// </summary>
        /// <param name="sendText"></param>
        /// <returns></returns>
        private ushort[] GetUshortData(string sendText)
        {
            List<byte> byteList = new List<byte>();
            byte[] textBytes = Encoding.ASCII.GetBytes(sendText);//将文本转换为字节数组
            byteList.AddRange(textBytes);
            //ushort 1个ushort=2字节
            if (textBytes.Length % 2 == 1)
            {
                byteList.Add(0x00);
            }

            List<ushort> ushortList = new List<ushort>();
            for (int i = 0; i < byteList.Count; i += 2)
            {
                byte[] item = new byte[2];
                item[0] = byteList[i + 1];
                item[1] = byteList[i];
                ushortList.Add(BitConverter.ToUInt16(item, 0));
            }

            return ushortList.ToArray();
        }

        private async void chkStateAll_Click(object sender, EventArgs e)
        {
            if (!serialPort.IsOpen)
                return;
            try
            {
                isWriting = true;
                CheckBox chk = sender as CheckBox;
                if (chk.Tag == null)
                    return;
                string tag = chk.Tag.ToString();// 地址或all
                if (tag == "all")
                {
                    bool blState1 = chkState01.Checked;
                    bool blState2 = chkState02.Checked;
                    bool blState3 = chkState03.Checked;
                    bool blState4 = chkState04.Checked;
                    bool blState5 = chkState05.Checked;
                    bool[] allStates = new bool[5] { blState1, blState2, blState3, blState4, blState5 };
                    //将所有灯的状态与统一控制的状态一致
                    for (int i = 0; i < allStates.Length; i++)
                    {
                        if (allStates[i] != chk.Checked)
                            allStates[i] = chk.Checked;
                    }
                    //写入
                    await master.WriteMultipleCoilsAsync(1, 0, allStates);
                    chkState01.Checked = chk.Checked;
                    chkState02.Checked = chk.Checked;
                    chkState03.Checked = chk.Checked;
                    chkState04.Checked = chk.Checked;
                    chkState05.Checked = chk.Checked;
                }
                else
                {
                    //单一控制
                    ushort addr = ushort.Parse(tag);//状态地址
                    var setState = chk.Checked;//写入的状态
                    chkStateAll.Checked = chkState01.Checked && chkState02.Checked && chkState03.Checked && chkState04.Checked && chkState05.Checked;
                    await master.WriteSingleCoilAsync(1, addr, setState);
                }

                isWriting = false;
            }
            catch (Exception ex)
            {

            }
        }

      
        private void checkBox123_CheckStateChanged(object sender, EventArgs e)
        {
            var chk = sender as CheckBox;//当前操作的复选框
            int index = int.Parse(chk.Tag.ToString());//一定要设置CheckBox的Tag属性
            pvOne.Model.Series[index].IsVisible = chk.Checked;//复选框勾选，则显示；否则隐藏
            pvOne.Model.InvalidatePlot(false);
        }
    }
}
